--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 2a7810ebf261ce1146f91d7346674e3063705709
Parents : 88d0b7c
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-04-03T13:12:31-05:00
feat(build): improve macOS build process with universal support and architecture-specific configurations
Changes
6 files changed, 104 insertions(+), 9 deletions(-)
Diff
diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml
index 5769b899..93686aca 100644
--- a/.github/workflows/build-release.yml
+++ b/.github/workflows/build-release.yml
@@ -98,6 +98,33 @@ jobs:
- name: Install dependencies
run: bash scripts/ci/github-install-deps.sh
+ - name: Install Rosetta (Apple Silicon)
+ if: matrix.label == 'macos'
+ run: /usr/sbin/softwareupdate --install-rosetta --agree-to-license || true
+
+ - name: Set up Python x64 for cx_Freeze universal slice
+ id: python_x64
+ if: matrix.label == 'macos'
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
+ with:
+ python-version: ${{ env.PYTHON_VERSION }}
+ architecture: x64
+ update-environment: false
+
+ - name: Export PYTHON_CMD_X64 for mac universal build
+ if: matrix.label == 'macos'
+ run: echo "PYTHON_CMD_X64=${{ steps.python_x64.outputs.python-path }}" >> "$GITHUB_ENV"
+
+ - name: Install project deps into x64 Python (mac universal cx_Freeze)
+ if: matrix.label == 'macos'
+ env:
+ PY_X64: ${{ steps.python_x64.outputs.python-path }}
+ run: |
+ set -euo pipefail
+ "$PY_X64" -m pip install -U pip setuptools wheel
+ "$PY_X64" -m pip install "cx-freeze>=7.0.0"
+ "$PY_X64" -m pip install -e .
+
- name: Build distributables
run: bash "${{ matrix.build_script }}"
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3e858c70..7d8854ab 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -76,5 +76,32 @@ jobs:
- name: Install dependencies
run: bash scripts/ci/github-install-deps.sh
+ - name: Install Rosetta (Apple Silicon)
+ if: matrix.label == 'macos'
+ run: /usr/sbin/softwareupdate --install-rosetta --agree-to-license || true
+
+ - name: Set up Python x64 for cx_Freeze universal slice
+ id: python_x64
+ if: matrix.label == 'macos'
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
+ with:
+ python-version: ${{ env.PYTHON_VERSION }}
+ architecture: x64
+ update-environment: false
+
+ - name: Export PYTHON_CMD_X64 for mac universal build
+ if: matrix.label == 'macos'
+ run: echo "PYTHON_CMD_X64=${{ steps.python_x64.outputs.python-path }}" >> "$GITHUB_ENV"
+
+ - name: Install project deps into x64 Python (mac universal cx_Freeze)
+ if: matrix.label == 'macos'
+ env:
+ PY_X64: ${{ steps.python_x64.outputs.python-path }}
+ run: |
+ set -euo pipefail
+ "$PY_X64" -m pip install -U pip setuptools wheel
+ "$PY_X64" -m pip install "cx-freeze>=7.0.0"
+ "$PY_X64" -m pip install -e .
+
- name: Build distributables
run: bash "${{ matrix.build_script }}"
diff --git a/forge.config.js b/forge.config.js
index 2969b812..7d599cff 100644
--- a/forge.config.js
+++ b/forge.config.js
@@ -2,7 +2,13 @@ const { FusesPlugin } = require("@electron-forge/plugin-fuses");
const { FuseV1Options, FuseVersion } = require("@electron/fuses");
const platform = process.env.PLATFORM || process.platform;
-const extraResourceDir = platform === "win32" || platform === "win" ? "build/exe/win32" : "build/exe/linux";
+const arch = process.env.ARCH || process.arch;
+let extraResourceDir = `build/exe/linux-${arch}`;
+if (platform === "win32" || platform === "win") {
+ extraResourceDir = `build/exe/win32-${arch}`;
+} else if (platform === "darwin") {
+ extraResourceDir = `build/exe/darwin-${arch}`;
+}
module.exports = {
packagerConfig: {
diff --git a/package.json b/package.json
index 3214c27d..f88ab69a 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,7 @@
"dist:zip": "pnpm run electron-postinstall && pnpm run build && electron-forge make --targets @electron-forge/maker-zip",
"dist-prebuilt": "pnpm run electron-postinstall && pnpm run build-backend && electron-builder --publish=never",
"dist:mac-arm64": "pnpm run electron-postinstall && pnpm run build && electron-builder --mac --arm64 --publish=never",
- "dist:mac-universal": "pnpm run electron-postinstall && pnpm run build && electron-builder --mac --universal --publish=never",
+ "dist:mac-universal": "bash scripts/build-macos-universal.sh",
"start": "pnpm run build && electron-forge start",
"package": "pnpm run build && electron-forge package",
"make": "pnpm run build && electron-forge make"
@@ -138,7 +138,7 @@
},
"extraResources": [
{
- "from": "build/exe/linux-${arch}",
+ "from": "build/exe/darwin-${arch}",
"to": "backend",
"filter": [
"**/*"
diff --git a/scripts/build-backend.js b/scripts/build-backend.js
index 22beb9da..3570e9e2 100755
--- a/scripts/build-backend.js
+++ b/scripts/build-backend.js
@@ -46,14 +46,19 @@ try {
const platform = process.env.PLATFORM || process.platform;
const arch = process.env.ARCH || process.arch;
const isWin = platform === "win32" || platform === "win";
+ const isDarwin = platform === "darwin";
const targetName = isWin ? "ReticulumMeshChatX.exe" : "ReticulumMeshChatX";
- // Create architecture-specific build directory
- const platformFolder = isWin ? "win32" : "linux";
+ let platformFolder = "linux";
+ if (isWin) {
+ platformFolder = "win32";
+ } else if (isDarwin) {
+ platformFolder = "darwin";
+ }
const buildDirRelative = `build/exe/${platformFolder}-${arch}`;
const buildDir = path.join(__dirname, "..", buildDirRelative);
- // Allow overriding the python command (e.g., to use wine python for cross-builds)
+ // Allow overriding the python command
const pythonCmd = process.env.PYTHON_CMD || "poetry run python";
console.log(
@@ -66,12 +71,23 @@ try {
CX_FREEZE_BUILD_EXE: buildDirRelative,
};
- // Split pythonCmd to handle arguments like "wine python"
- const cmdParts = pythonCmd.split(" ");
+ const cmdParts = pythonCmd.trim().split(/\s+/).filter(Boolean);
const cmd = cmdParts[0];
const args = [...cmdParts.slice(1), "cx_setup.py", "build"];
- const result = spawnSync(cmd, args, {
+ let spawnCmd = cmd;
+ let spawnArgs = args;
+ const rosettaX64 =
+ isDarwin &&
+ arch === "x64" &&
+ process.arch === "arm64" &&
+ !process.env.PYTHON_CMD;
+ if (rosettaX64) {
+ spawnCmd = "arch";
+ spawnArgs = ["-x86_64", cmd, ...args];
+ }
+
+ const result = spawnSync(spawnCmd, spawnArgs, {
stdio: "inherit",
shell: false,
env: env,
diff --git a/scripts/build-macos-universal.sh b/scripts/build-macos-universal.sh
new file mode 100644
index 00000000..ebf7f5c8
--- /dev/null
+++ b/scripts/build-macos-universal.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# Build darwin-arm64 and darwin-x64 cx_Freeze backends, then electron-builder --mac --universal.
+# On Apple Silicon, the x64 backend must be built with an x86_64 Python (e.g. Homebrew in /usr/local).
+# Set PYTHON_CMD_X64 to that interpreter if Poetry's default env is arm64-only.
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$ROOT"
+
+pnpm run electron-postinstall
+pnpm run version:sync
+pnpm run build-frontend
+cross-env ARCH=arm64 pnpm run build-backend
+if [[ -n "${PYTHON_CMD_X64:-}" ]]; then
+ cross-env ARCH=x64 PYTHON_CMD="$PYTHON_CMD_X64" pnpm run build-backend
+else
+ cross-env ARCH=x64 pnpm run build-backend
+fi
+exec pnpm exec electron-builder --mac --universal --publish=never
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────